#
# This file is a part of the open-eBackup project.
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
# If a copy of the MPL was not distributed with this file, You can obtain one at
# http://mozilla.org/MPL/2.0/.
#
# Copyright (c) [2024] Huawei Technologies Co.,Ltd.
#
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
#

import ctypes
import os
import time

from dataclasses import dataclass
from enum import Enum
from common.env_common import adaptation_win_path
from common.util.scanner_utils import get_log_level
from exchange import log
from exchange.commons.const import DataBackupType


class VolumeBackupConfC(ctypes.Structure):
    _fields_ = [
        ("backupType", ctypes.c_int),
        ("copyFormat", ctypes.c_int),
        ("copyName", ctypes.c_char_p),
        ("volumePath", ctypes.c_char_p),
        ("prevCopyMetaDirPath", ctypes.c_char_p),
        ("outputCopyDataDirPath", ctypes.c_char_p),
        ("outputCopyMetaDirPath", ctypes.c_char_p),
        ("blockSize", ctypes.c_uint32),
        ("sessionSize", ctypes.c_uint64),
        ("hasherNum", ctypes.c_uint32),
        ("hasherEnabled", ctypes.c_bool),
        ("enableCheckpoint", ctypes.c_bool)
    ]


class TaskStatisticsC(ctypes.Structure):
    _fields_ = [
        ("bytesToRead", ctypes.c_uint64),
        ("bytesRead", ctypes.c_uint64),
        ("blocksToHash", ctypes.c_uint64),
        ("blocksHashed", ctypes.c_uint64),
        ("bytesToWrite", ctypes.c_uint64),
        ("bytesWritten", ctypes.c_uint64)
    ]


@dataclass
class BackupConfig:
    """
    增量备份相关参数
    """
    # 备份类型: 0-全备，1-增备
    backup_type: DataBackupType = DataBackupType.FULL_BACKUP
    # 源文件路径
    source_file_full_path: bytes = None
    # 备份文件类型, 默认6：FILE
    copy_format: int = 6
    # 备份后的文件名
    target_file_name: bytes = None
    # 备份之后的data仓路径
    output_copy_data_dir_path: bytes = None
    # 备份之后的meta仓路径
    output_copy_meta_dir_path: bytes = None
    # 增量备份时，传入的上次的meta仓路径
    prev_copy_meta_dir_path: bytes = None


@dataclass
class TaskStatistics:
    bytes_to_read: int
    bytes_read: int
    blocks_to_hash: int
    blocks_hashed: int
    bytes_to_write: int
    bytes_written: int


class TaskStatus(Enum):
    """
    备份任务状态
    """
    INIT = 0
    RUNNING = 1
    SUCCEED = 2
    ABORTING = 3
    ABORTED = 4
    FAILED = 5


class BackupTask:
    def __init__(self, backup_config: BackupConfig):
        self.__init_shared_lib()
        log_path = os.getenv("GENERALDB_LOG_PATH")
        log_level = get_log_level()
        self._shared_lib.InitLogs(log_path.encode(), log_level)
        config = VolumeBackupConfC(
            backupType=backup_config.backup_type,
            volumePath=backup_config.source_file_full_path,
            copyFormat=backup_config.copy_format,
            copyName=backup_config.target_file_name,
            prevCopyMetaDirPath=backup_config.prev_copy_meta_dir_path,
            outputCopyDataDirPath=backup_config.output_copy_data_dir_path,
            outputCopyMetaDirPath=backup_config.output_copy_meta_dir_path,
            # 块大小默认4MB，必选参数，不支持修改
            blockSize=4096 * 1024,
            sessionSize=1024 * 1024 * 1024 * 100,
            hasherNum=12,
            hasherEnabled=True,
            enableCheckpoint=False
        )
        self.instance = self._shared_lib.BuildBackupTask(config)

    def valid(self) -> bool:
        return self.instance is not None

    def start(self) -> bool:
        return self._shared_lib.StartTask(self.instance)

    def destroy(self) -> None:
        self._shared_lib.DestroyTask(self.instance)

    def statistics(self) -> TaskStatistics:
        _task_statistics_c = self._shared_lib.GetTaskStatistics(self.instance)
        return TaskStatistics(bytes_to_read=_task_statistics_c.bytesToRead,
                              bytes_read=_task_statistics_c.bytesRead,
                              blocks_to_hash=_task_statistics_c.blocksToHash,
                              blocks_hashed=_task_statistics_c.blocksHashed,
                              bytes_to_write=_task_statistics_c.bytesToWrite,
                              bytes_written=_task_statistics_c.bytesWritten)

    def abort(self) -> None:
        self._shared_lib.DestroyTask(self.instance)

    def status(self) -> TaskStatus:
        return self._shared_lib.GetTaskStatus(self.instance)

    def is_failed(self) -> bool:
        return self._shared_lib.IsTaskFailed(self.instance)

    def is_terminated(self) -> bool:
        return self._shared_lib.IsTaskTerminated(self.instance)

    def do_backup(self) -> bool:
        if not self.valid():
            log.error("init edb backup task failed.")
            return False
        if not self.start():
            log.error("start edb backup task failed.")
            return False
        while not self.is_terminated():
            log.debug("waiting edb backup task finished.")
            time.sleep(1)
        if self.is_failed():
            log.error("edb backup task failed.")
            return False
        return True

    def __init_shared_lib(self):
        lib_path = f"{adaptation_win_path()}/DataBackup/ProtectClient/Plugins/GeneralDBPlugin/bin"
        self._shared_lib = ctypes.cdll.LoadLibrary(
            os.path.join(lib_path, "FS_Backup.dll"))
        # Define function prototypes
        self._shared_lib.BuildBackupTask.restype = ctypes.c_void_p
        self._shared_lib.BuildBackupTask.argtypes = [VolumeBackupConfC]
        self._shared_lib.InitLogs.argtypes = [ctypes.c_char_p, ctypes.c_int]
        self._shared_lib.StartTask.argtypes = [ctypes.c_void_p]
        self._shared_lib.StartTask.restype = ctypes.c_bool
        self._shared_lib.DestroyTask.argtypes = [ctypes.c_void_p]
        self._shared_lib.GetTaskStatistics.argtypes = [ctypes.c_void_p]
        self._shared_lib.GetTaskStatistics.restype = TaskStatisticsC
        self._shared_lib.AbortTask.argtypes = [ctypes.c_void_p]
        self._shared_lib.GetTaskStatus.argtypes = [ctypes.c_void_p]
        self._shared_lib.GetTaskStatus.restype = TaskStatus
        self._shared_lib.IsTaskFailed.argtypes = [ctypes.c_void_p]
        self._shared_lib.IsTaskFailed.restype = ctypes.c_bool
        self._shared_lib.IsTaskTerminated.argtypes = [ctypes.c_void_p]
        self._shared_lib.IsTaskTerminated.restype = ctypes.c_bool
